// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Diagnostics;
using System.Windows.Forms;
using AxWMPLib;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.Objects;
using svs.Mobile.BeyondMedia.Properties;
using svs.Mobile.BeyondMedia.UiCore;
using WMPLib;
using _WMPOCXEvents_KeyPressEventHandler=AxWMPLib._WMPOCXEvents_KeyPressEventHandler;
using _WMPOCXEvents_PlayStateChangeEventHandler=AxWMPLib._WMPOCXEvents_PlayStateChangeEventHandler;

namespace svs.Mobile.BeyondMedia.Controls.Players.WindowsMediaPlayer
{
	internal class WindowsMediaPlayerControl : AxWindowsMediaPlayer, IPlayerControl
	{
		#region Events

		public event EventHandler BackClick;
		public event EventHandler StreamPositionChange;
		public event EventHandler PlayError;
		public event EventHandler MediaEnd;
		public event EventHandler StartPlaying;
		public event EventHandler Paused;
		public event EventHandler Stopped;

		#endregion

		#region Data Members

		private Timer m_PositionUpdateTimer;
		private Timer m_PositionThrottleTimer;
		private Track m_CurrentTrack;
		private static int REFRSEH_LONG = 10*1000;
		private double m_AutoSkip;
		private static int REFRESH_SHORT = 1*1000;

		#endregion

		#region Constructors

		public WindowsMediaPlayerControl()
		{
			m_PositionThrottleTimer = new Timer();
			m_PositionThrottleTimer.Tick += new EventHandler(OnPositionThrottleTick);
			m_PositionThrottleTimer.Interval = 500;
			KeyPressEvent += new _WMPOCXEvents_KeyPressEventHandler(OnPlayerKeyPress);
			m_PositionUpdateTimer = new Timer();
			m_PositionUpdateTimer.Tick += new EventHandler(OnPositionUpdateTick);
			ErrorEvent += new EventHandler(OnPlayerError);
			PlayStateChange += new _WMPOCXEvents_PlayStateChangeEventHandler(OnPlayStateChanged);

		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Changes the volume by an increment.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		public void ChangeVolume(Helpers.MoveDirection i_direction)
		{
			if (i_direction == Helpers.MoveDirection.Forward)
			{
				if (VolumeLevel + 10 < 100)
					VolumeLevel += 10;
				else
					VolumeLevel = 100;
			}
			else
			{
				if (VolumeLevel - 10 > 0)
					VolumeLevel -= 10;
				else
					VolumeLevel = 0;
			}
		}

		/// <summary>
		/// Skips in the specified i_direction.
		/// </summary>
		/// <param name="i_direction">The i_direction.</param>
		/// <param name="i_duration">The i_duration.</param>
		public void Skip(Helpers.MoveDirection i_direction, int i_duration)
		{
			if (i_direction == Helpers.MoveDirection.Forward)
				if (CurrentStreamPosition + i_duration < CurrentStreamDuration)
					SetCurrentPositionThrottled(CurrentStreamPosition + i_duration);
				else
				{
					SetCurrentPositionThrottled(CurrentStreamDuration - 1);
					Helpers.Beep();
				}
			else if (CurrentStreamPosition - i_duration > 0)
				SetCurrentPositionThrottled(CurrentStreamPosition - i_duration);
			else
			{
				SetCurrentPositionThrottled(0);
				Helpers.Beep();
			}
		}

		/// <summary>
		/// Pauses the playback.
		/// </summary>
		public void Pause()
		{
			try
			{
				Ctlcontrols.pause();
			}
			catch
			{
				//Internet stream can't pause
				Ctlcontrols.stop();
			}
		}

		/// <summary>
		/// Resumes the the playback.
		/// </summary>
		public void Play()
		{
			try
			{
				if (CurrentTrack != null && CurrentTrack.Exists)
					Ctlcontrols.play();
				else
					Helpers.Beep();
			}
			catch
			{
				Helpers.Beep();
			}
		}

		/// <summary>
		/// Stops the playback.
		/// </summary>
		public void Stop()
		{
			try
			{
				Ctlcontrols.stop();
			}
			catch
			{
				Helpers.Beep();
			}
		}

		/// <summary>
		/// Plays the track.
		/// </summary>
		/// <param name="track">The track.</param>
		public void PlayTrack(Track track)
		{
			try
			{
				CoreHelper.WriteTraceEntry("About to play track. Player is: " + playState.ToString());
				//If we are still plying or paused
				if (playState != WMPPlayState.wmppsStopped)
				{
					CloseStream();
				}

				CurrentTrack = track;
				CoreHelper.WriteTraceEntry("Starting track: " + CurrentTrack.TrackPath);
				URL = CurrentTrack.TrackPath;
			}
			catch
			{
				throw new ApplicationException(Error.get_Item(0).errorDescription);
			}
		}

		/// <summary>
		/// Closes the media file.
		/// </summary>
		public void CloseStream()
		{
			CoreHelper.WriteTraceEntry("Closing current stream");

			if (IsPlaying)
			{
				Pause();
				//Wait to publis the status change events
				for (int i = 0; i != 10; i++)
					Application.DoEvents();
			}

			if (IsPlaying || IsPaused)
			{
				Stop();
				//Wait to publis the status change events
				for (int i = 0; i != 10; i++)
					Application.DoEvents();
			}
		}

		/// <summary>
		/// Saves the current position in the current file.
		/// </summary>
		public void SaveCurrentPosition(Track.PlayMode i_mode)
		{
			if (CurrentTrack != null && !CurrentTrack.IsPlayed)
			{
				if (CurrentStreamDuration > 0)
				{
					CurrentTrack.TotalTime = CurrentStreamDuration;

					//Position has meaning only if we know the duration
					if (CurrentStreamPosition > 0)
						CurrentTrack.PlayedTime = CurrentStreamPosition;
				}

				try
				{
					if (CurrentTrack.HasGeneratedName)
					{
						if (currentPlaylist != null && currentPlaylist.name.Length > 0)
							CurrentTrack.Name = currentPlaylist.name;
						else if (Ctlcontrols.currentItem.name.Length > 0)
							CurrentTrack.Name = Ctlcontrols.currentItem.name;
					}
				}
				catch
				{
				}

				try
				{
					CurrentTrack.Protocol = network.sourceProtocol;
				}
				catch
				{
				}
			}
		}

		/// <summary>
		/// Called when the Application is closing.
		/// </summary>
		public void OnClosing()
		{
			m_PositionUpdateTimer.Enabled = false;
			m_PositionUpdateTimer.Dispose();

			CoreHelper.WriteTraceEntry("Closing Player...");

			CloseStream();

			CoreHelper.WriteTraceEntry("Player Status at Exit: " + status);

			System.Runtime.InteropServices.Marshal.ReleaseComObject(ocx);

			Dispose();

			CoreHelper.WriteTraceEntry("Closed!");
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Gets the current track.
		/// </summary>
		/// <value>The current track.</value>
		public Track CurrentTrack
		{
			get { return m_CurrentTrack; }
			set
			{
				if(value == null)
					close(); //Unlock the current track

				 m_CurrentTrack = value;
			}
		}

		/// <summary>
		/// Gets a value indicating whether this instance is playing.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is playing; otherwise, <c>false</c>.
		/// </value>
		public bool IsPlaying
		{
			get { return playState == WMPPlayState.wmppsPlaying; }
		}

		/// <summary>
		/// Gets a value indicating whether this instance is paused.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is playing; otherwise, <c>false</c>.
		/// </value>
		public bool IsPaused
		{
			get { return playState == WMPPlayState.wmppsPaused; }
		}

		/// <summary>
		/// Gets or sets the current stream position.
		/// </summary>
		/// <value>The current stream position.</value>
		public double CurrentStreamPosition
		{
			get
			{
				try
				{
					return Ctlcontrols.currentPosition;
				}
				catch
				{
					return -1;
				}
			}
			set
			{
				try
				{
					CoreHelper.WriteTraceEntry("Changing current position to: " + value.ToString());
					Ctlcontrols.currentPosition = value;
				}
				catch
				{
					Helpers.Beep();
				}
			}
		}

		/// <summary>
		/// Gets the current stream duration.
		/// </summary>
		/// <value>The current stream duration.</value>
		public double CurrentStreamDuration
		{
			get
			{
				try
				{
					IWMPMedia item = Ctlcontrols.currentItem;
					return item.duration;
				}
				catch (Exception ex)
				{
					Debug.WriteLine(ex);
					return -1;
				}
			}
		}

		/// <summary>
		/// Gets the version of windows media player.
		/// </summary>
		/// <value>The WMP version.</value>
		public string PlayerVersion
		{
			get { return versionInfo; }
		}

		/// <summary>
		/// Sets teh volume level
		/// </summary>
		public int VolumeLevel
		{
			get { return settings.volume; }
			set
			{
				if (value >= 0 && value <= 100)
					settings.volume = value;
			}
		}

		public void OnSkinChanged(ISkin i_skin)
		{
			MessageBoxEx.Show(Resources.SkinChangedMessage, Resources.SkinChangedTitle,
											MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
		}

		#endregion

		#region Private Methods

		private void OnPlayStateChanged(object sender, _WMPOCXEvents_PlayStateChangeEvent e)
		{
			CoreHelper.WriteTraceEntry("Player State Changed to: " + (WMPPlayState) e.newState + " Pos: " + CurrentStreamPosition +
			                        ", Dur:" + CurrentStreamDuration);

			if (e.newState == (int) WMPPlayState.wmppsMediaEnded)
			{
				try
				{
					//Media players version 10.1.X send OnMediaEnded by mistake at the begining of a play
					if (IsOldPlayerVersion() && CurrentStreamPosition == 0)
						return;
				}
				catch
				{
					return;
				}

				OnMediaEnded();
			}
			else if (e.newState == (int) WMPPlayState.wmppsPlaying)
			{
				if (playState == WMPPlayState.wmppsStopped)
				{
					if (CurrentTrack.PlayedTime > 0 && (CurrentTrack.TotalTime == 0 || CurrentTrack.PlayedTime < CurrentTrack.TotalTime))
					{
						//Helpers.Beep();
						m_AutoSkip = CurrentTrack.PlayedTime;
					}
				}

				if (m_AutoSkip > 0)
				{
					try
					{
						CoreHelper.WriteTraceEntry("Autoskipping to: " + m_AutoSkip);
						Ctlcontrols.currentPosition = m_AutoSkip;
						m_AutoSkip = 0;
					}
					catch
					{
					}
				}
				else
					OnStartPlaying();
			}

			else if (e.newState == (int) WMPPlayState.wmppsPaused)
			{
				OnPause();
			}

			else if (e.newState == (int) WMPPlayState.wmppsStopped)
			{
				//Media players version 10.1.X do not send OnMediaEnded at the end of the track
				if (IsOldPlayerVersion() && CurrentStreamDuration == -1 && CurrentTrack.TrackType == Track.StreamType.LocalFile)
					OnMediaEnded();
				else
					OnStop();
			}
		}

		private void OnMediaEnded()
		{
			if (MediaEnd != null)
				MediaEnd(this, null);
		}

		private void OnStartPlaying()
		{
			if (StartPlaying != null)
				StartPlaying(this, null);

			m_PositionUpdateTimer.Interval = REFRESH_SHORT;
			m_PositionUpdateTimer.Enabled = true;
		}

		private void OnPause()
		{
			if (Paused != null)
				Paused(this, null);

			m_PositionUpdateTimer.Enabled = false;
		}

		private void OnStop()
		{
			if (Stopped != null)
				Stopped(this, null);

			m_PositionUpdateTimer.Enabled = false;
		}

		private void OnPlayerError(object sender, EventArgs e)
		{
			if (PlayError != null)
			{
				PlayError(this, null);
			}
		}

		private void OnPositionUpdateTick(object sender, EventArgs e)
		{
			if (StreamPositionChange != null)
			{
				StreamPositionChange(this, null);
			}

			m_PositionUpdateTimer.Interval = REFRSEH_LONG;
		}

		private void OnPlayerKeyPress(object sender, _WMPOCXEvents_KeyPressEvent e)
		{
			if (e.nKeyAscii == (Char) Keys.Escape)
			{
				if (BackClick != null)
					BackClick(this, null);
			}
		}

		private void OnPositionThrottleTick(object sender, EventArgs e)
		{
			m_PositionThrottleTimer.Enabled = false;
		}

		private void SetCurrentPositionThrottled(double newPos)
		{
			CurrentStreamPosition = newPos;
		}

		/// <summary>
		/// Determines whether [is old player version].
		/// </summary>
		/// <returns>
		/// 	<c>true</c> if [is old player version]; otherwise, <c>false</c>.
		/// </returns>
		private bool IsOldPlayerVersion()
		{
			return PlayerVersion.StartsWith("10.1.");
		}

		#endregion
	}
}